package com.hands.netty.netty;



import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.hands.commen.entity.netty.ClientInfo;
import com.hands.commen.entity.netty.MsgHeader;
import com.hands.commen.entity.netty.Us;
import com.hands.commen.utils.Base64;
import com.hands.commen.utils.ZipUtils;
import com.hands.netty.service.impl.WxRpcServiceImpl;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.util.concurrent.GlobalEventExecutor;
import org.apache.dubbo.rpc.AsyncContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;



import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * @Title: NioServerHandler
 * @Description: 服务业务实现
 * @date 2018/6/415:29
 */
@Service
public class NioServerHandler  extends SimpleChannelInboundHandler<Object>  {

    public static ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);


    public  static Map<String, Us> mapString = new HashMap<String, Us>();

    public  static List<ClientInfo> Client=new ArrayList<ClientInfo>();

    
    private static final Logger logger = LoggerFactory.getLogger(NioServerHandler.class);

    public static Cache<String, JSONObject> cache = CacheBuilder.newBuilder()
            .expireAfterWrite(1L, TimeUnit.MINUTES)  //写入1分钟后过期
            .maximumSize(50000L)
            .build();

    public  static String END_STRING="$/r$\r\n";;




    /**
     *读取客户端发来的数据
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
    	List<String> person=new ArrayList<>();
    	logger.info("请求的数据{}",msg);
    	JSONObject jsonObject=new JSONObject();
    	if(msg !=null) {
    		try {
    			String message = (String) msg;
    			String stringMsg = message.toString();
    			boolean contains =stringMsg.contains(END_STRING);
    			if(contains) {
    				String[] split = stringMsg.split("\\$");
    				stringMsg=split[0];
    			}
    			jsonObject = JSON.parseObject(stringMsg);
    		}catch (Exception e) {
    			logger.info(""+"不是监测数据"+ e);	
			}
        	if(jsonObject.get("header")==null || "".equals(jsonObject.get("bizCode"))) {
        		logger.info(""+"不是监测数据"+ msg.toString()+" "+ new Date());
        	}else {
        		String header = jsonObject.get("header").toString();
            	//获取header中的bizCode
            	JSONObject jsonHeader = JSON.parseObject(header);
            	if(jsonHeader.get("bizCode")==null || "".equals(jsonHeader.get("bizCode"))) {
            		logger.info(""+"不是监测数据"+ msg.toString()+" "+ new Date());
            		ctx.writeAndFlush("Received your message : " + msg.toString());
            		ctx.close();
            	}
            	//根据不用协议处理数据
            	else {
            		//登录处理（111）
            		if("111".equals(jsonHeader.get("bizCode").toString())) {
            			handLogin(jsonObject,ctx);
            		}else
            		//心跳检测（109）
            		if("109".equals(jsonHeader.get("bizCode").toString())) {
            			logger.info("心跳检测=={}",jsonObject);
            			heartbeat(jsonObject,ctx);
            		}else
            		 //108协议(连接边缘网关)
            		if("108".equals(jsonHeader.get("bizCode").toString())) {
            			logger.info("连接边缘网关=={}",jsonObject);
            			edgegateway(jsonObject,ctx);
            		}
            		//token更新（107）
            		else if("107".equals(jsonHeader.get("bizCode").toString())) {
            			logger.info("token更新=={}",jsonObject);
            			handToken(jsonObject,ctx);
            		}
            		//广播信息变更新
            		else if("100".equals(jsonHeader.get("bizCode").toString()) && (!"".equals(jsonHeader.get("mac").toString()))){
            			logger.info("广播信息变更新{}",jsonObject);
            			updateInfoByNews(jsonObject,ctx);
            		}
            		//广播注册
            		else if("106".equals(jsonHeader.get("bizCode").toString())) {
            			logger.info("广播注册{}",jsonObject);
            			handBadio(jsonObject,ctx);
            		}
            		//业务处理
            		else {
            			//token校验
            			MsgHeader myHeader =JSONObject.toJavaObject(JSON.parseObject(jsonObject.get("header").toString()), MsgHeader.class);
            			Us us = mapString.get(myHeader.getMac());
            			boolean isrun= cheakToken(jsonObject,ctx);
            			logger.info("进来业务处理{}",isrun);
            			if(isrun) {
            				///http请求发过来
            				if(myHeader.getToUserId().contains("#http")) {
            					logger.info("微信收到的数据{}",jsonObject);
								handWxReturn(myHeader,jsonObject);
            				} else if("26".equals(jsonHeader.get("bizCode").toString())){
            					soundInfoHand(jsonObject,ctx);
							} else  {
            					if(!"".equals(myHeader.getToUserId())) {
            						Us usinfo = mapString.get(myHeader.getToUserId());
            					    if(usinfo!=null) {
            					    	setCleitMsgByString(usinfo.getCtx(), jsonObject.toString());
            					    }
            					}
            				}
            			}
            			
            		}
            	}
        	}
    	}  
    }

	/**
	 * 微信转发信息
	 * @param myHeader
	 * @param jsonObject
	 */
	private void handWxReturn(MsgHeader myHeader, JSONObject jsonObject) {

		AsyncContext asyncContext = WxRpcServiceImpl.asyncContextMap.getIfPresent(myHeader.getMsgId());
		if(asyncContext !=null){
			asyncContext.signalContextSwitch();
			asyncContext.write(jsonObject.toString());
			WxRpcServiceImpl.asyncContextMap.invalidate(myHeader.getMsgId());
		}

	}

	private void soundInfoHand(JSONObject jsonObject, ChannelHandlerContext ctx) {
		Object boby = jsonObject.get("body");

		BobyDecode(boby.toString());

	}


	//广播消息更新(110)
	private void updateInfoByNews(JSONObject jsonObject, ChannelHandlerContext ctx) {
		String body = jsonObject.get("body").toString();
    	MsgHeader myHeader =JSONObject.toJavaObject(JSON.parseObject(jsonObject.get("header").toString()), MsgHeader.class);
		for (int i = 0; i < Client.size(); i++)
        {
			ClientInfo clientInfo = Client.get(i);
            if (clientInfo.getMac() == null)
            {
            	Client.remove(i);
                continue;
            }
            logger.info("缓存中的mac{}，数据传输的Mac{}",clientInfo.getMac(),myHeader.getMac());
            if (clientInfo.getMac().equals(myHeader.getMac()))
            {
                for (int k=0;k< clientInfo.getClientlist().size();k++)
                {
                	//广播消息
                    relay(clientInfo.getClientlist().get(k), jsonObject);
                }  
                break;
            }
        }
		
	}


	//广播信息
	private void relay(String user, JSONObject jsonObject) {
		user = user.replace("%", "");
		List<Us> likeByMap = getLikeByMap(mapString,user);
		for(int i=0;i<likeByMap.size();i++) {
			ChannelHandlerContext ctx = likeByMap.get(i).getCtx();
			setCleitMsgByString(ctx,jsonObject.toString());
		}
	}


	
	/**
     * Map集合模糊匹配
     * @param map  map集合
     * @param keyLike  模糊key
     * @return
     */
    public static List<Us> getLikeByMap(Map<String, Us> map, String keyLike){
        List<Us> list=new ArrayList<>();
        for (Map.Entry<String, Us> entity : map.entrySet()) {
            if(entity.getKey().indexOf(keyLike)>-1){
                list.add(entity.getValue());
            }

        }
        return list;
    }


	//广播注册(106)
    private void handBadio(JSONObject jsonObject, ChannelHandlerContext ctx) {
    	
    	String body = jsonObject.get("body").toString();
    	MsgHeader myHeader =JSONObject.toJavaObject(JSON.parseObject(jsonObject.get("header").toString()), MsgHeader.class);
    	int len = body.indexOf("clientId") +11;
        String tmp = body.substring(len,body.indexOf("\"}")-len);
        Boolean isok = false;
        ///消息广播客户端注册
        for (int i = 0; i < Client.size(); i++)
        {
             ClientInfo clientInfo = Client.get(i);
            if (clientInfo.mac==null)
            {
            	Client.remove(i);
                continue;
            }
            if (clientInfo.mac == myHeader.getMac())
            {
                if (!clientInfo.Clientlist.contains(tmp)){
					clientInfo.Clientlist.add(tmp);
				}

                isok = true;
                break;
            }
        }
        if (!isok)
        {
            ClientInfo clienInfo = new ClientInfo();
            clienInfo.setMac(myHeader.getMac());
            List<String> arrayList = new ArrayList<String>();
            arrayList.add(tmp);
            clienInfo.setClientlist(arrayList);
            Client.add(clienInfo);
        }
        
        logger.info("消息广播客户端注册成功{}",Client);
		
	}



	//token校验
    private boolean cheakToken(JSONObject jsonObject, ChannelHandlerContext ctx) {

    	MsgHeader myHeader =JSONObject.toJavaObject(JSON.parseObject(jsonObject.get("header").toString()), MsgHeader.class);
		
		logger.info("传过来的数据{}",myHeader);
    	
    		if(!"".equals(myHeader.getToken()) ) {
    			//
    			Us us = mapString.get(myHeader.getToUserId());
    			if(us !=null) {
    				return true;
    			}else{
					Us usMac = mapString.get(myHeader.getMac());
					if(usMac !=null) {
						return true;
					}
				}
    			//微信端请求
    		
    	}
		return false;
	}



	//心跳检测（109）
    private void heartbeat(JSONObject jsonObject, ChannelHandlerContext ctx) {
    	MsgHeader myHeader =JSONObject.toJavaObject(JSON.parseObject(jsonObject.get("header").toString()), MsgHeader.class);
		Us us = mapString.get(myHeader.getMac());
		Object boby = jsonObject.get("body");
		if(us !=null) {
			if(us.getCtx()==ctx) {
				us.setHearttime(new Date());
				if(us.getErrortime()>5) {
					us.setErrortime(0);
					String token = CreateToken();
					MsgHeader msgHeader = new  MsgHeader();
					msgHeader.setBizCode(107);
					msgHeader.setOs(1);
					msgHeader.setToken(token);
					msgHeader.setRespCode(200);
					setCleitMsg(ctx, msgHeader, null);
				}else {
					myHeader.setRespCode(200);
					setCleitMsg(ctx, myHeader,boby);	
				}
				us.setErrortime(us.getErrortime()+1);
			}else {
				setCleitMsgByString(ctx, jsonObject.toString());
			}
		}
	}

    //创建token
    private String CreateToken() {
    	String s= UUID.randomUUID().toString();
		String replace = s.replace("-", "");
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		Date now = new Date();
		Date afterDate = new Date(now .getTime() + 600000);
		long time = afterDate.getTime();
		String token=replace+"&"+time;
    	return token;
    }
    

	//token更新（107）
    private void handToken(JSONObject jsonObject, ChannelHandlerContext ctx) {
		// TODO Auto-generated method stub
    	//token创建
    	String token = CreateToken();
    	Object boby = jsonObject.get("body");
    	MsgHeader myHeader =JSONObject.toJavaObject(JSON.parseObject(jsonObject.get("header").toString()), MsgHeader.class);
    	myHeader.setToken(token);
    	myHeader.setBizCode(109);
    	myHeader.setOs(1);
    	myHeader.setRespCode(200);
    	setCleitMsg(ctx,myHeader,boby);
	}


    //登录请求处理（111）
	private void handLogin(JSONObject jsonObject, ChannelHandlerContext ctx) {
    	logger.info("登录请求参数=={}",jsonObject);
		//处理是否已经存在登录信息
    	MsgHeader myHeader =JSONObject.toJavaObject(JSON.parseObject(jsonObject.get("header").toString()), MsgHeader.class);
		Us us = mapString.get(myHeader.getMac());
		//token创建
		String token = CreateToken();
		Object boby = jsonObject.get("body");
		if(us !=null) {
			if(myHeader.getOs()==2) {
				us.setCtx(ctx); //把之前的通道进行覆盖
			}	
		}else {
			//配置缓存信息
			Us usInfo = new Us();
			usInfo.setCtx(ctx);
			usInfo.setUser(myHeader.getMac());
			usInfo.setMac(myHeader.getMac());
			usInfo.setToken(token);
			mapString.put(myHeader.getMac(), usInfo);
			us=usInfo;
		}
		//服务端
		if(myHeader.getOs()==0) {
			Boolean isok = false;
            Boolean isserver = false;
			 //判断是不是服务器的111登录，touserid和mac地址是一致的
            if(myHeader.getToUserId().equals(myHeader.getMac())){
            	logger.info("服务端请求登录");
                Us usInfo = mapString.get(myHeader.getMac());
                if(us !=null){
                	isok=true;
                	isserver=true;
                	us.setOs(0);
                	if(us.getToken()==null) {
                		us.setCtx(ctx);
                	}else {
                		//校验心跳是否已经超时
                		Date hearttime = us.getHearttime();
                	    //时间差大于一分钟(消息通道覆盖)
                		us.setCtx(ctx);
                		us.setHearttime(new Date());
                	}
                	us.setToken(token);
                }
            }else {

            	Us usInfoMac = mapString.get(myHeader.getMac());
				logger.info("服务端返回客服端请求登录的信息{},{}",usInfoMac,myHeader);
            	if(usInfoMac !=null){
            			isok=true;
            	}
            	
            	
            }
            //发送107
            if(isok) {
//				logger.info("进来发送登录信息给服务端{},{}",myHeader.getToUserId(),us.getUser());
//            	if(myHeader.getToUserId().equals(us.getUser())) {
                	MsgHeader msgHeader = new MsgHeader();
                	msgHeader.setBizCode(107);
                	msgHeader.setOs(1);
                	//msgHeader.setRespCode(200);
                	msgHeader.setToken(token);
                	setCleitMsg(us.getCtx(),msgHeader,null);
                	us.setToken(token);
                	
                		Us usToUser = mapString.get(myHeader.getToUserId());
                    	if(usToUser !=null) {
                    		logger.info("发送的用户{}",myHeader.getToUserId());
                    		setCleitMsgByString(usToUser.getCtx(), jsonObject+END_STRING);
                	      }
                	
                	
            	//}
            }
            
		}else {
			//
			Us toUserId = mapString.get(myHeader.getToUserId());
			if(toUserId !=null) {
				logger.info("请求转发到服务端处理{}",myHeader.getToUserId());
				setCleitMsgByString(toUserId.getCtx(),jsonObject.toString());
			}else {
				myHeader.setRespCode(500);
				myHeader.setOs(1);
				myHeader.setRespMsg("主机不在线，请检查主机");
			    logger.info("主机不在线，请检查主机");
				setCleitMsg(us.getCtx(), myHeader, boby);
			}
		}
		
		
	}


	//108协议(边缘网关)
    private void edgegateway(JSONObject jsonObject,ChannelHandlerContext ctx) {
    	MsgHeader myHeader =JSONObject.toJavaObject(JSON.parseObject(jsonObject.get("header").toString()), MsgHeader.class);
    	Object boby = jsonObject.get("body");
    	logger.info("边缘处理的网关数据=={},{}",myHeader,mapString);
		//缓存为空
		if(mapString.isEmpty()) {
			myHeader.setRespCode(401);
		}else {
			Us us = mapString.get(myHeader.getMac());
			if(us !=null && myHeader.getToken().equals(us.getToken())) {
				//把当前消息通道进行赋值
				us.setCtx(ctx);
				myHeader.setRespCode(200);
			}else {
				myHeader.setRespCode(401);
			}
		}
		setCleitMsg(ctx,myHeader,boby);
	}

    
    /**
     * boby数据解析
     */ 
    private  JSONObject BobyDecode(String boby) {
    	JSONObject parseObject=null;
    	if(boby !=null) {
    		byte[] decode = Base64.decode(boby);
    		String uncompress = ZipUtils.uncompress(decode);
    		if(uncompress !=null) {
    			parseObject = JSON.parseObject(uncompress);
    		}
    	}
    	return parseObject;
    }
    

	/*    *//**
     * 读取完毕客户端发送过来的数据之后的操作
     */
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {

    	
    	//ctx.writeAndFlush("Even"+new Date());
        // channels.add(ctx.channel());

//          ctx.channel().write("123456");
//          ctx.channel().flush();
    }

    /**
     * 客户端主动断开服务端的链接,关闭流
     * */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        
        logger.info("通道不活跃");
        //getKey(ctx.toString());
        ctx.close();
    	
    }

    //删除缓存
    private static String getKey(String value){  
        String key="";  
        for (Entry<String, Us> entry : mapString.entrySet()) {  
        	logger.info("删除通道缓存");
        	Us us = entry.getValue();
        	if(value.equals(us.getCtx())) {
        		key=entry.getKey();
        		mapString.remove(key);
        	}
            
        }  
        return key;  
    }
   

	/**
     * 客户端主动连接服务端
     * */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {

    	logger.info("客户端主动连接服务端");
        ctx.writeAndFlush("Even$/r$\r\n");
        super.channelActive(ctx);
    }

    /**
     * 首次连接获取客户端ID保存
     */
    private void clientId(ChannelHandlerContext ctx) {
    	String info  =ctx.toString();
    	int index = info.indexOf(":");
        String str=info.substring(index + 1);
        String[] strlist = str.split(",");
        System.out.println(strlist[0].trim());
        NioServer.map.put(strlist[0].trim(), ctx);
        channels.add(ctx.channel());
    }
    
    
    /**
     * 发生异常处理
     * */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        cause.printStackTrace();
         logger.error("连接异常,连接异常："+new Date()+cause.getMessage(), cause);
        ctx.fireExceptionCaught(cause);
           removeChannnelMap(ctx);
          ctx.close();
    }

    /**
     *删除map中ChannelHandlerContext
     *  */
    private void removeChannnelMap(ChannelHandlerContext ctx){
        for( String key :NioServer.map.keySet()){
            if( NioServer.map.get(key)!=null &&  NioServer.map.get(key).equals( ctx)){
                NioServer.map.remove(key);
                logger.info("删除成功{}",key);
              
            }
        }
    }
  
    /**
     * 发送数据
     * @param smg
     */
    public void setMsg(String smg){
        logger.info("发送的消息{}",smg);
        channels.writeAndFlush(smg);

    }


    /**
     * 指定客戶端(消息发送)
     */
    public void setCleitMsg(ChannelHandlerContext channelHandlerContext,Object header,Object boby){
        HashMap<String, Object> map = new HashMap<>();
		map.put("header", header);
		if(boby !=null) {
			map.put("boby", boby);
		}
		//发送的数据
		String smg=JSON.toJSONString(map, SerializerFeature.WriteMapNullValue)+END_STRING;
		logger.info("发送的数据包==>{}",smg);
		channelHandlerContext.writeAndFlush(smg);
    	

    }
    
    //原数据返回
    public void setCleitMsgByString(ChannelHandlerContext channelHandlerContext,String msg){
		
    	channelHandlerContext.writeAndFlush(msg+END_STRING);
    	logger.info("发送的数据{}",msg+END_STRING);
    }
    

    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        super.userEventTriggered(ctx, evt);
        if (evt instanceof IdleStateEvent) {
            IdleStateEvent event = (IdleStateEvent) evt;
            if (event.state().equals(IdleState.READER_IDLE)){
                ctx.close();
            }

            //标志该链接已经close 了 
        }
    }

}